模板方法模式
前面的内容都是关于封装的,包括封装对象创建、方法掉有那个、复杂接口等。
这里介绍的模板方法模式同样是用于封装,封装的对象是算法块,使得子类可以任何时候将自己挂进运算里,无需重写算法流程。
模板方法模式在一个方法中定义一个算法的骨架,而将一些步骤延迟到子类中。模板方法使得子类可以在不改变算法结构的情况下,重新插入新的算法实现。
模板就是一个方法,更具体地说,这个方法将算法定义一组步骤,其中的任何步骤都可以是抽象的,由子类负责实现。这样可以确保算法的结构保持不变,同时由子类提供部分实现。
注意到上面的类提供了一个钩子,钩子是一种被声明在抽象类中的方法,但是只有空的或默认的实现。钩子的存在可以让子类有能力对算法的不同点进行挂钩,子类也可以不选择挂钩。
1 | public abstract class CaffeineBeverageWithHook { |
为了使用钩子,我们可以在子类覆盖它。在上面的例子中,钩子用来决定是否执行模板方法的某部分算法。
1 | public class CoffeeWithHook extends CaffeineBeverageWithHook { |
模板方法同样应用了一个设计原则:好莱坞原则。
别调用我们,我们会调用你。
好莱坞原则可以防止依赖腐败。当高层组件依赖底层组件,而底层组件又依赖高层组件,而高层组件有依赖边侧组件,而边侧组件有依赖底层组件时,依赖腐败就产生了。
在好莱坞原则下,我们允许底层组件将自己挂钩到系统上,但是有高层组件巨鼎什么时候和怎样使用这些底层组件。换句话说,也就是,高层组件对待低层组件的方式是”别调用我们,我们会调用你”。
当我们设计模板方法模式时,我们告诉子,”不要调用我们,我们会调用你。”
在Java中一个实际的例子是sort()方法的实现,sort原则上允许对任何类型的数组进行排序,只要该类实现了compareTo()方法,也就是在sort中使用模板方法,在实际使用中调用相应类的实现。
1 | public class Duck implements Comparable { |
1 | public class DuckSortTestDrive { |
模板方法、策略、工厂方法比较:
- 模板方法:子类决定如何实现算法中的某些步骤。
- 策略:封装可互换的行为,然后使用委托来决定要采用哪一个行为。
- 工厂方法:由子类决定实例化哪个具体类。